package org.pcub.extension;

import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.data.Openable;
import org.bukkit.block.data.type.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.*;
import org.bukkit.event.player.*;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.scheduler.BukkitRunnable;
import org.geysermc.floodgate.util.DeviceOs;
import org.pcub.extension.Common.State;
import org.pcub.extension.common.CheckPlayerCrosshair;
import org.pcub.extension.common.OperationLimiter;
import org.pcub.extension.feature.*;

import java.util.Set;
import java.util.UUID;

public class EventListener implements Listener {
    private final Common common = Common.getInstance();
    private final Main main = common.main;
    private final CheckPlayerCrosshair checkCross = new CheckPlayerCrosshair();
    private final UseItemToRun useItemToRun = new UseItemToRun();
    private final FastSkill fastSkill = new FastSkill();
    private final DropLimiter dropLimiter = new DropLimiter();
    private final OperationLimiter<Player> leftClickLimiter = new OperationLimiter<>();
    private final ChestMenu chestMenu = new ChestMenu();
    private final Stacker stacker = new Stacker(chestMenu);


    // 玩家进服事件
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        Player player = event.getPlayer();
        String playerDisplay = player.getDisplayName();
        UUID playerUUID = player.getUniqueId();
        String playerUUIDStr = playerUUID.toString();
        boolean isGeyser = common.geyserValid && common.geyserApi.isBedrockPlayer(playerUUID);
        boolean isFloodgate = common.floodgateValid && common.floodgateApi.isFloodgatePlayer(playerUUID);
        // 当 Floodgate 登录出现异常时踢出玩家
        if (common.floodgateValid && !isFloodgate && isGeyser) {
            player.kickPlayer("登录系统出现异常， 请重新加入服务器。");
            main.logger.info("\n" + playerDisplay + " 因为 Floodgate 登录异常无法正常加入游戏");
            common.setTempScore("login_status", playerUUIDStr, 0);
        } else {
            common.setTempScore("is_touch", playerUUIDStr, (
                isFloodgate && Set.of(
                    DeviceOs.GOOGLE,
                    DeviceOs.IOS,
                    DeviceOs.WINDOWS_PHONE,
                    DeviceOs.AMAZON
                ).contains(common.floodgateApi.getPlayer(playerUUID).getDeviceOs()) ||
                !common.floodgateValid && isGeyser
            ) ? 1 : 0);
            common.setTempScore("login_status", playerUUIDStr, (isFloodgate || isGeyser) ? 2 : 1);
            common.setTempScore("clicked", playerUUIDStr, 0);
            common.setTempScore("inventory_opened", playerUUIDStr, 0);
            fastSkill.cancelSneak(player);
            String edition = (isFloodgate || isGeyser) ? "bedrock" : "java";
            new BukkitRunnable(){
                @Override
                public void run(){
                    common.consoleExec("execute as " + playerUUIDStr + " run function #pcub:player_join/" + edition);
                }
            }.runTaskLater(main, 0L);
        }
    }



    // 容器打开事件
    @EventHandler
    public void onInventoryOpen(InventoryOpenEvent event) {
        Player player = (Player) event.getPlayer();
        chestMenu.open(player.getName(), player.getUniqueId().toString());
    }



    // 容器点击事件
    @EventHandler
    public void onInventoryClick(InventoryClickEvent event) {
        // 获取玩家
        Player player = (Player) event.getWhoClicked();
        String playerName = player.getName();
        UUID playerUUID = player.getUniqueId();
        String playerUUIDStr = playerUUID.toString();
        // 获取事件类型
        InventoryAction currentAction = event.getAction();
        ClickType currentClick = event.getClick();
        Inventory currentInv = event.getClickedInventory();
        InventoryType currentInvType = (currentInv != null) ? currentInv.getType() : null;
        int currentSlot = event.getSlot();
        // 获取指针物品和槽位物品
        Stacker.StackerCommon current = new Stacker.StackerCommon(event.getCurrentItem());
        Stacker.StackerCommon cursor = new Stacker.StackerCommon(event.getCursor());

        // 调试信息
        if (common.debug) common.debugLogger(playerName + " " + currentClick + " " + currentInvType + " " + currentSlot + " " + currentAction + " 指针:" + cursor.type + " 槽位:" + current.type);

        if(common.legacyStack) {
            stacker.legacyStack(
                    event,
                    player,
                    playerName,
                    playerUUIDStr,
                    currentAction,
                    currentClick,
                    currentInv,
                    currentInvType,
                    currentSlot,
                    current,
                    cursor
            );
            return;
        }

        // 末影箱菜单
        if (    common.getScore("screen", playerName) >= 0 &&
                chestMenu.checkWork(cursor, current, player).limit) event.setCancelled(true);
        // 强制合并（常规交换）
        else if (currentAction == InventoryAction.SWAP_WITH_CURSOR) {
            int margeResult = new Stacker.MergeItem(current, cursor).checkWork();
            if (margeResult > 0 && common.debug) common.debugLogger(playerName + " 强制交换合并 x" + margeResult);
        }
    }



    // 容器关闭事件
    @EventHandler
    public void onInventoryClose(InventoryCloseEvent event) {
        HumanEntity humanEntity = event.getPlayer();
        if (humanEntity instanceof Player) {
            chestMenu.close(humanEntity.getName(), humanEntity.getUniqueId().toString());
        }
    }



    // 玩家主手切换
    @EventHandler
    public void onPlayerItemHeld(PlayerItemHeldEvent event) {
        Player player = event.getPlayer();
        // 触发数据包侧的物品检测接口
        player.addScoreboardTag("pcub_inventory_check");
    }



    // 玩家交互事件
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent event) {
        Player targetPlayer = event.getPlayer();
        String targetName = targetPlayer.getName();
        UUID targetIDN = targetPlayer.getUniqueId();
        String targetID = targetIDN.toString();
        boolean isBedrock =
                common.geyserValid &&
                common.geyserApi.isBedrockPlayer(targetIDN)
            ||
                common.floodgateValid &&
                common.floodgateApi.isFloodgatePlayer(targetIDN);
        ItemStack usedItem = event.getItem();
        Action action = event.getAction();
        Block clickedBlock = event.getClickedBlock();

        // 调试
        if (common.debug) common.debugLogger(targetName + " " + action + " " + ((usedItem != null) ? usedItem.getType() : "") + " " + ((clickedBlock != null) ? clickedBlock.getType() : ""));

        /* 触发 RIGHT_CLICK_BLOCK 的操作：
            圆环 长按空气（目标居画面中心）
            准星 右键方块（目标居画面中心）
            准星 空 右键方块（目标居画面中心）
          以下类型随后还会触发 LEFT_CLICK_*：
            圆环 短按方块（目标居点击处）
            圆环 空 短按方块（目标居点击处）
            圆环 长按方块（目标居画面中心）
            准星 空 右键方块（仅交互类；目标居画面中心）
            准星 右键方块（仅交互类；目标居画面中心）
        */
        /* 触发 RIGHT_CLICK_AIR 的操作：
            圆环 长按空气
            准星 右键空气
          以下类型随后还会触发 LEFT_CLICK_*：
            圆环 长按方块
        */
        /* 触发 LEFT_CLICK_BLOCK 的操作：
            圆环 空/小概率 长按方块（目标为 AIR 或居画面中心）
            准星 左键方块（目标居画面中心）
            准星 空 左键方块（目标居画面中心）
        */
        /* 触发 LEFT_CLICK_AIR 的操作：
            圆环 空 长按方块
            圆环 短按空气
            圆环 空 短按空气
            准星 左键空气
            准星 空 左键空气
        */

        // 是否为使用物品
        boolean canUseItem = action == Action.RIGHT_CLICK_AIR;

        if (action == Action.RIGHT_CLICK_BLOCK) {
            boolean clickInTarget = true;
            Material targetMat = (clickedBlock != null) ? clickedBlock.getType() : Material.AIR;
            String targetStr = targetMat.name();
            // 限制可能紧跟其后的 LEFT_CLICK_* 动作
            if (isBedrock) {
                leftClickLimiter.put(targetPlayer, 4L);
            }
            // 交互模式判断（准星/圆环）
            if (isBedrock && clickedBlock != null) {
                clickInTarget = clickedBlock.getLocation().equals(targetPlayer.getTargetBlock(null, 5).getLocation());
                if (clickInTarget) {
                    checkCross.resetCondition(targetIDN);
                } else {
                    checkCross.determineFalseWhenReach(targetIDN, 4);
                }
            }
            boolean notCrosshair = isBedrock && checkCross.not(targetIDN);
            // 潜行 / 非准星点击画面中心 / 目标方块不可交互
            canUseItem = targetPlayer.isSneaking() || notCrosshair && clickInTarget || !(
                    targetMat == Material.DISPENSER ||
                    targetMat == Material.NOTE_BLOCK ||
                    targetMat == Material.DROPPER ||
                    targetMat == Material.JUKEBOX ||
                    targetMat == Material.HOPPER ||
                    targetMat == Material.CHEST ||
                    targetMat == Material.ENDER_CHEST ||
                    targetMat == Material.TRAPPED_CHEST ||
                    targetMat.data == Switch.class ||
                    Openable.class.isAssignableFrom(targetMat.data) ||
                    targetStr.endsWith("SIGN"));
            // 方块交互相关
            if (!targetPlayer.isSneaking() || usedItem == null) {
                // 取消冒险玩家的食用蛋糕、破坏花盆操作
                // TODO: 不再内置
                if ((targetStr.startsWith("POTTED_") || targetMat == Material.CAKE) && targetPlayer.getGameMode() == GameMode.ADVENTURE) {
                    event.setCancelled(true);
                }
                // 开启钱庄箱子
                else if (targetMat == Material.ENDER_CHEST) chestMenu.readyOpen(targetName, targetID);
                // 漏斗经 Geyser 方块映射后，准星模式下打开漏斗和使用物品会同时触发
                // 例如手持书本点击漏斗，会同时弹出两个界面，严重时将无法打开任何容器
                // TODO: 在 Geyser 中修复映射后的方块的交互事件
                else if (isBedrock && !notCrosshair && targetMat == Material.HOPPER &&
                        switch (event.getMaterial()) {
                            case WRITTEN_BOOK, SNOWBALL, SPLASH_POTION, BOW, CROSSBOW -> true;
                            default -> false;
                        }
                ) {
                    targetPlayer.sendMessage("§7暂不支持手持书本、雪球、药水、弓打开漏斗。");
                    event.setCancelled(true); // 基岩版普通书本，以及使用物品执行命令，不受事件取消影响
                    if (common.debug) common.debugLogger(targetName + " 阻止手持书本/雪球/药水/弓打开漏斗，避免冲突");
                }
            }
        } else if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) {
            // 仅限圆环长按
            canUseItem = isBedrock && leftClickLimiter.get(targetPlayer) < 1 && checkCross.not(targetIDN);
        }

        // 物品使用相关
        if (canUseItem) {
            ItemMeta usedMeta = (usedItem != null) ? usedItem.getItemMeta() : null;
            Material usedType = (usedItem != null) ? usedItem.getType() : null;
            // 雪球、丹药投掷限制
            if (action != Action.LEFT_CLICK_AIR && (
                    usedType == Material.SNOWBALL ||
                    usedType == Material.SPLASH_POTION
                ) && dropLimiter.check(
                        targetPlayer,
                        targetName,
                        usedItem,
                        isBedrock,
                        common.getTempScore("is_touch", targetID) == 1
                    ).limit
            ) event.setCancelled(true);
            // 使用物品执行命令
            State shortcutResult = useItemToRun.checkCommandExecute(targetPlayer, usedMeta, isBedrock);
            if (shortcutResult.limit) event.setCancelled(true);
            // 基岩版副手功能
            if (!shortcutResult.success && isBedrock) {
                new BukkitRunnable() {
                    @Override
                    public void run() {
                        useItemToRun.bedrockOffhand(targetPlayer, usedType);
                    }
                }.runTaskAsynchronously(main);
            }
        }
    }



    // 玩家退出
    @EventHandler
    public void onPlayerQuit(PlayerQuitEvent event) {
        // 重置交互模式判断
        checkCross.determineTrue(event.getPlayer().getUniqueId());
    }



    @EventHandler
    public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
        Entity entity = event.getRightClicked();
        Player player = event.getPlayer();
        if (
            entity.getType() == EntityType.VILLAGER &&
            VillagerFeature.beforeOpen(player, entity, common).limit
        ) event.setCancelled(true);
    }



    // 玩家切换潜行
    @EventHandler
    public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
        new BukkitRunnable(){
            @Override
            public void run() {
                fastSkill.check(event.getPlayer(), event.isSneaking());
            }
        }.runTaskAsynchronously(main);
    }
}
